#ifndef __open_vip_tilelink__
#define __open_vip_tilelink__

#include "xspcomm/xcoroutine.h"
#include "xspcomm/xport.h"
#include "xspcomm/xclock.h"
#include "xspcomm/xcallback.h"
#include <list>
#include <string>

namespace ovip {

enum class TLChannel {
    A,
    B,
    C,
    D,
    E,
    F, // default be false
};
std::string str_TLChannel(TLChannel c)
{
    switch (c) {
    case TLChannel::A: return "TLChannel.A";
    case TLChannel::B: return "TLChannel.B";
    case TLChannel::C: return "TLChannel.C";
    case TLChannel::D: return "TLChannel.D";
    case TLChannel::E: return "TLChannel.E";
    case TLChannel::F: return "TLChannel.F";
    default: Assert(false, "not valid TLChannel: %d", (int)c);
    }
    return "Error!";
}

enum class TLOperation {
    Get,
    Put,
    Atomic,
    Intent,
    Acquire,
    Release,
};
std::string str_TLOperation(TLOperation o)
{
    switch (o) {
    case TLOperation::Get: return "TLOperation.Get";
    case TLOperation::Put: return "TLOperation.Put";
    case TLOperation::Atomic: return "TLOperation.Atomic";
    case TLOperation::Intent: return "TLOperation.Intent";
    case TLOperation::Acquire: return "TLOperation.Acquire";
    case TLOperation::Release: return "TLOperation.Release";
    default: Assert(false, "not valid TLOperation: %d", (int)o);
    }
    return "Error!";
}

enum class TLPermission {
    // permission
    Invalid = 0,
    Branch  = 1,
    Trunk   = 2,
    Tip     = 3,
};
std::string str_TLPermission(TLPermission p)
{
    switch (p) {
    case TLPermission::Invalid: return "TLPermission.Invalid";
    case TLPermission::Branch: return "TLPermission.Branch";
    case TLPermission::Trunk: return "TLPermission.Trunk";
    case TLPermission::Tip: return "TLPermission.Tip";
    default: Assert(false, "not valid TLOperation: %d", (int)p);
    }
    return "Error!";
}

enum class TLParamType {
    None,
    Cap,
    Grow,
    Shrink_Report,
    Logical,
    Arithmetic,
    Intent
};
enum class TLParam {
    // Cap type
    toT = 0,
    toB = 1,
    toN = 2,
    // Grow type
    NtoB = 0,
    NtoT = 1,
    BtoT = 2,
    // Shrink type
    TtoB = 0,
    TtoN = 1,
    BtoN = 2,
    // report type
    TtoT = 3,
    BtoB = 4,
    NtoN = 5,
    // Logical
    XOR  = 0,
    OR   = 1,
    AND  = 2,
    SWAP = 3,
    // Arithmetic
    MIN  = 0,
    MAX  = 1,
    MINU = 2,
    MAXU = 3,
    ADD  = 4,
    // Intent
    PrefetchRead  = 0,
    PrefetchWrite = 1,
};

std::string str_TLParam(u_int8_t _p, TLParamType type)
{
    TLParam p = (TLParam)_p;
    switch (type) {
    case TLParamType::None: {
        return xspcomm::sFmt("0x%lx", _p);
    }
    case TLParamType::Intent: {
        switch (p) {
        case TLParam::PrefetchRead: return "PrefetchRead";
        case TLParam::PrefetchWrite: return "PrefetchWrite";
        default: Assert(false, "invalid TLParamType::Intent: %d", (int)p);
        }
    }
    case TLParamType::Cap: {
        switch (p) {
        case TLParam::toT: return "toT";
        case TLParam::toB: return "toB";
        case TLParam::toN: return "toN";
        default: Assert(false, "invalid TLParamType::Cap: %d", (int)p);
        }
    }
    case TLParamType::Grow: {
        switch (p) {
        case TLParam::NtoB: return "NtoB";
        case TLParam::NtoT: return "NtoT";
        case TLParam::BtoT: return "BtoT";
        default: Assert(false, "invalid TLParamType::Grow: %d", (int)p);
        }
    }
    case TLParamType::Shrink_Report: {
        switch (p) {
        case TLParam::TtoB: return "TtoB";
        case TLParam::TtoN: return "TtoN";
        case TLParam::BtoN: return "BtoN";
        case TLParam::TtoT: return "TtoT";
        case TLParam::BtoB: return "BtoB";
        case TLParam::NtoN: return "NtoN";
        default:
            Assert(false, "invalid TLParamType::Shrink_Report: %d", (int)p);
        }
    }
    case TLParamType::Logical: {
        switch (p) {
        case TLParam::XOR: return "XOR";
        case TLParam::OR: return "OR";
        case TLParam::AND: return "AND";
        case TLParam::SWAP: return "SWAP";
        default: Assert(false, "invalid TLParamType::Logical: %d", (int)p);
        }
    }
    case TLParamType::Arithmetic: {
        switch (p) {
        case TLParam::MIN: return "MIN";
        case TLParam::MAX: return "MAX";
        case TLParam::MINU: return "MINU";
        case TLParam::MAXU: return "MAXU";
        case TLParam::ADD: return "ADD";
        default: Assert(false, "invalid TLParamType::Arithmetic: %d", (int)p);
        }
    }
    default: Assert(false, "invalid TLParamType: %d", (int)type);
    }
    return "Error!";
}

enum class TLMessageA {
    // channle A
    // Message,  Opcode, Operation, Response
    PutFullData     = 0, // Put     AccessAck
    PutPartialData  = 1, // Put     AccessAck
    ArithmeticLogic = 2, // Atomic  AccessAckData
    LogicalData     = 3, // Atomic  AccessAckData
    Get             = 4, // Get     AccessAckData
    Intent          = 5, // Intent  HintAck
    AcquireBlock    = 6, // Acquire Grant or GrantData
    AcquirePerm     = 7, // Acquire Grant
};
std::string str_TLMessageA(u_int8_t v)
{
    auto a = (TLMessageA)v;
    switch (a) {
    case TLMessageA::PutFullData: return "TLMessageA.PutFullData";
    case TLMessageA::PutPartialData: return "TLMessageA.PutPartialData";
    case TLMessageA::ArithmeticLogic: return "TLMessageA.ArithmeticLogic";
    case TLMessageA::LogicalData: return "TLMessageA.LogicalData";
    case TLMessageA::Get: return "TLMessageA.Get";
    case TLMessageA::Intent: return "TLMessageA.Intent";
    case TLMessageA::AcquireBlock: return "TLMessageA.AcquireBlock";
    case TLMessageA::AcquirePerm: return "TLMessageA.AcquirePerm";
    default: Assert(false, "not valid TLMessageA: %d", (int)a);
    }
    return "Error!";
}

enum class TLMessageB {
    // channel B
    // Message,  Opcode, Operation, Response
    PutFullData    = 0, // Put AccessAck
    PutPartialData = 1, // Put AccessAck
    ArithmeticData = 2, // Atomic AccessAckData
    LogicalData    = 3, // Atomic AccessAckData
    Get            = 4, // Get AccessAckData
    Intent         = 5, // Intent HintAck
    ProbeBlock     = 6, // Probe ProbeAck or ProbeAckData
    ProbePerm      = 7, // Probe ProbeAck
};
std::string str_TLMessageB(u_int8_t v)
{
    auto b = (TLMessageB)v;
    switch (b) {
    case TLMessageB::PutFullData: return "TLMessageB.PutFullData";
    case TLMessageB::PutPartialData: return "TLMessageB.PutPartialData";
    case TLMessageB::ArithmeticData: return "TLMessageB.ArithmeticData";
    case TLMessageB::LogicalData: return "TLMessageB.LogicalData";
    case TLMessageB::Get: return "TLMessageB.Get";
    case TLMessageB::Intent: return "TLMessageB.Intent";
    case TLMessageB::ProbeBlock: return "TLMessageB.ProbeBlock";
    case TLMessageB::ProbePerm: return "TLMessageB.ProbePerm";
    default: Assert(false, "not valid TLMessageB: %d", (int)b);
    }
    return "Error!";
}

enum class TLMessageC {
    // channel C
    // Message,  Opcode, Operation, Response
    AccessAck     = 0, // Put
    AccessAckData = 1, // Get or Atomic
    HintAck       = 2, // Intent
    ProbeAck      = 4, // Probe
    ProbeAckData  = 5, // Probe
    Release       = 6, // Release ReleaseAck
    ReleaseData   = 7, // Release ReleaseAck

};
std::string str_TLMessageC(u_int8_t v)
{
    auto c = (TLMessageC)v;
    switch (c) {
    case TLMessageC::AccessAck: return "TLMessageC.AccessAck";
    case TLMessageC::AccessAckData: return "TLMessageC.AccessAckData";
    case TLMessageC::HintAck: return "TLMessageC.HintAck";
    case TLMessageC::ProbeAck: return "TLMessageC.ProbeAck";
    case TLMessageC::ProbeAckData: return "TLMessageC.ProbeAckData";
    case TLMessageC::Release: return "TLMessageC.Release";
    case TLMessageC::ReleaseData: return "TLMessageC.ReleaseData";
    default: Assert(false, "not valid TLMessageC: %d", (int)c);
    }
    return "Error!";
}

bool tl_message_c_need_ret(xspcomm::XPort &p)
{
    return p["c_bits_opcode"] == TLMessageC::Release
           || p["c_bits_opcode"] == TLMessageC::ReleaseData;
}

enum class TLMessageD {
    // channel D
    // Message,  Opcode, Operation, Response
    AccessAck     = 0, // Put
    AccessAckData = 1, // Get or Atomic
    HintAck       = 2, // Intent
    Grant         = 4, // Acquire GrantAck
    GrantData     = 5, // Acquire GrantAck
    ReleaseAck    = 6, // Release
};
std::string str_TLMessageD(u_int8_t v)
{
    auto d = (TLMessageD)v;
    switch (d) {
    case TLMessageD::AccessAck: return "TLMessageD.AccessAck";
    case TLMessageD::AccessAckData: return "TLMessageD.AccessAckData";
    case TLMessageD::HintAck: return "TLMessageD.HintAck";
    case TLMessageD::Grant: return "TLMessageD.Grant";
    case TLMessageD::GrantData: return "TLMessageD.GrantData";
    case TLMessageD::ReleaseAck: return "TLMessageD.ReleaseAck";
    default: Assert(false, "not valid TLMessageD: %d", (int)d);
    }
    return "Error!";
}

bool tl_message_d_is_request(xspcomm::XPort &p, u_int64_t source)
{
    return (p["d_bits_opcode"] == TLMessageD::Grant
            || p["d_bits_opcode"] == TLMessageD::GrantData)
           && p["d_bits_source"] == source;
    ;
}

bool tl_message_d_is_ack(xspcomm::XPort &p, u_int64_t source)
{
    return (p["d_bits_opcode"] == TLMessageD::AccessAck
            || p["d_bits_opcode"] == TLMessageD::AccessAckData
            || p["d_bits_opcode"] == TLMessageD::HintAck
            || p["d_bits_opcode"] == TLMessageD::ReleaseAck)
           && p["d_bits_source"] == source;
}

enum class TLMessageE {
    // channel E
    // Message,  Opcode, Operation, Response
    GrantAck = 0, // Acquire,
};
std::string str_TLMessageE()
{
    return "TLMessageE.GrantAck";
}

#ifndef TLDataMaskS
#define TLDataMaskS 1 // default u32 => 32*8 Bytes
#define TLDataSizeS TLDataMaskS * sizeof(u_int32_t) * 8
#endif

class TLData
{
public:
    TLChannel __channel = TLChannel::F;
    bool __valid        = false;
    virtual std::string AsString() { return "Empty TLData(F)"; }
    u_int8_t opcode = 0; // 3
};

typedef struct sTLDataA : public TLData {
    sTLDataA() { this->__channel = TLChannel::A; };
    u_int8_t param              = 0;   // 3
    u_int8_t size               = 0;   // z
    u_int64_t source            = 0;   // o
    u_int64_t address           = 0;   // a
    u_int32_t mask[TLDataMaskS] = {0}; // w
    u_int8_t data[TLDataSizeS]  = {0}; // 8w
    u_int8_t corrupt            = 0;   // 1
    u_int8_t valid              = 0;   // 1
    u_int8_t ready              = 0;   // 1
    std::string AsString()
    {
        return "\nopcode: " + str_TLMessageA(opcode) + "\n"
               + "param:  " + xspcomm::sFmt("0x%x", param) + "\n"
               + "size:   " + xspcomm::sFmt("0x%x", size) + "\n"
               + "source: " + xspcomm::sFmt("0x%x", source) + "\n"
               + "address:" + xspcomm::sFmt("0x%x", address) + "\n" + "mask:   "
               + xspcomm::sArrayHex((unsigned char *)mask, TLDataMaskS * 4)
               + "\n" + "data:   "
               + xspcomm::sArrayHex((unsigned char *)data, TLDataSizeS) + "\n"
               + "corrupt:" + xspcomm::sFmt("0x%x", corrupt) + "\n"
               + "valid:  " + xspcomm::sFmt("0x%x", valid) + "\n"
               + "ready:  " + xspcomm::sFmt("0x%x", ready) + "\n";
    }
} TLDataA, *pTLDataA;

void tla_2_port(TLDataA a, xspcomm::XPort &port)
{
    port["a_bits_opcode"]  = a.opcode;
    port["a_bits_param"]   = a.param;
    port["a_bits_address"] = a.address;
    port["a_bits_size"]    = a.size;
    port["a_bits_source"]  = a.source;
    port["a_bits_data"].SetBits(a.data, TLDataSizeS);
    port["a_bits_mask"].SetBits((u_int8_t *)a.mask, sizeof(a.mask));
    port["a_bits_corrupt"] = a.corrupt;
    port["a_valid"]        = 0;
}

TLDataA port_2_tla(xspcomm::XPort &port)
{
    TLDataA a;
    a.opcode  = port["a_bits_opcode"];
    a.param   = port["a_bits_param"];
    a.address = port["a_bits_address"];
    a.size    = port["a_bits_size"];
    a.source  = port["a_bits_source"];
    port["a_bits_data"].GetBits((u_int32_t *)a.data, TLDataSizeS / 4);
    port["a_bits_mask"].GetBits((u_int32_t *)a.mask, TLDataMaskS);
    a.corrupt = port["a_bits_corrupt"];
    a.valid   = port["a_valid"];
    a.ready   = port["a_ready"];
    return a;
}

typedef struct sTLDataB : public TLData {
    sTLDataB() { this->__channel = TLChannel::B; };
    u_int8_t param              = 0;   // 3
    u_int8_t size               = 0;   // z
    u_int64_t source            = 0;   // o  => FIXME
    u_int64_t address           = 0;   // a
    u_int32_t mask[TLDataMaskS] = {0}; // w
    u_int8_t data[TLDataSizeS]  = {0}; // 8w
    u_int8_t corrupt            = 0;   // 1
    u_int8_t valid              = 0;   // 1
    u_int8_t ready              = 0;   // 1
    std::string AsString()
    {
        return "\nopcode: " + str_TLMessageB(opcode) + "\n"
               + "param:  " + xspcomm::sFmt("0x%x", param) + "\n"
               + "size:   " + xspcomm::sFmt("0x%x", size) + "\n"
               + "source: " + xspcomm::sFmt("0x%x", source) + "\n"
               + "address:" + xspcomm::sFmt("0x%x", address) + "\n" + "mask:   "
               + xspcomm::sArrayHex((unsigned char *)mask, TLDataMaskS * 4)
               + "\n" + "data:   "
               + xspcomm::sArrayHex((unsigned char *)data, TLDataSizeS) + "\n"
               + "corrupt:" + xspcomm::sFmt("0x%x", corrupt) + "\n"
               + "valid:  " + xspcomm::sFmt("0x%x", valid) + "\n"
               + "ready:  " + xspcomm::sFmt("0x%x", ready) + "\n";
    }
} TLDataB, *pTLDataB;

void tlb_2_port(TLDataB b, xspcomm::XPort &port)
{
    port["b_bits_opcode"]  = b.opcode;
    port["b_bits_param"]   = b.param;
    port["b_bits_address"] = b.address;
    port["b_bits_size"]    = b.size;
    port["b_bits_source"]  = b.source;
    port["b_bits_data"].SetBits(b.data, TLDataSizeS);
    port["b_bits_mask"].SetBits((u_int8_t *)b.mask, sizeof(b.mask));
    port["b_bits_corrupt"] = b.corrupt;
    port["b_valid"]        = 0;
}

TLDataB port_2_tlb(xspcomm::XPort &port)
{
    TLDataB b;
    b.opcode  = port["b_bits_opcode"];
    b.param   = port["b_bits_param"];
    b.address = port["b_bits_address"];
    b.size    = port["b_bits_size"];
    b.source  = port["b_bits_source"];
    port["b_bits_data"].GetBits((u_int32_t *)b.data, TLDataSizeS / 4);
    port["b_bits_mask"].GetBits((u_int32_t *)b.mask, TLDataMaskS);
    b.corrupt = port["b_bits_corrupt"];
    b.valid   = port["b_valid"];
    b.ready   = port["b_ready"];
    return b;
}

typedef struct sTLDataC : public TLData {
    sTLDataC() { this->__channel = TLChannel::C; };
    u_int8_t param             = 0;   // 3
    u_int8_t size              = 0;   // z
    u_int64_t source           = 0;   // o
    u_int64_t address          = 0;   // a
    u_int8_t data[TLDataSizeS] = {0}; // 8w
    u_int8_t corrupt           = 0;   // 1
    u_int8_t valid             = 0;   // 1
    u_int8_t ready             = 0;   // 1
    std::string AsString()
    {
        return "\nopcode: " + str_TLMessageC(opcode) + "\n"
               + "param:  " + xspcomm::sFmt("0x%x", param) + "\n"
               + "size:   " + xspcomm::sFmt("0x%x", size) + "\n"
               + "source: " + xspcomm::sFmt("0x%x", source) + "\n"
               + "address:" + xspcomm::sFmt("0x%x", address) + "\n" + "data:   "
               + xspcomm::sArrayHex((unsigned char *)data, TLDataSizeS) + "\n"
               + "corrupt:" + xspcomm::sFmt("0x%x", corrupt) + "\n"
               + "valid:  " + xspcomm::sFmt("0x%x", valid) + "\n"
               + "ready:  " + xspcomm::sFmt("0x%x", ready) + "\n";
    }
} TLDataC, *pTLDataC;

void tlc_2_port(TLDataC c, xspcomm::XPort &port)
{
    port["c_bits_opcode"]  = c.opcode;
    port["c_bits_param"]   = c.param;
    port["c_bits_address"] = c.address;
    port["c_bits_size"]    = c.size;
    port["c_bits_source"]  = c.source;
    port["c_bits_data"].SetBits(c.data, TLDataSizeS);
    port["c_bits_corrupt"] = c.corrupt;
    port["c_valid"]        = 0;
}

TLDataC port_2_tlc(xspcomm::XPort &port)
{
    TLDataC c;
    c.opcode  = port["c_bits_opcode"];
    c.param   = port["c_bits_param"];
    c.address = port["c_bits_address"];
    c.size    = port["c_bits_size"];
    c.source  = port["c_bits_source"];
    port["c_bits_data"].GetBits((u_int32_t *)c.data, TLDataSizeS / 4);
    c.corrupt = port["c_bits_corrupt"];
    c.valid   = port["c_valid"];
    c.ready   = port["c_ready"];
    return c;
}

typedef struct sTLDataD : public TLData {
    sTLDataD() { this->__channel = TLChannel::D; };
    u_int8_t param             = 0;   // 3
    u_int8_t size              = 0;   // z
    u_int64_t source           = 0;   // o
    u_int8_t sink              = 0;   // i
    u_int8_t denied            = 0;   // 1
    u_int8_t data[TLDataSizeS] = {0}; // 8w
    u_int8_t corrupt           = 0;   // 1
    u_int8_t valid             = 0;   // 1
    u_int8_t ready             = 0;   // 1
    std::string AsString()
    {
        return "\nopcode: " + str_TLMessageD(opcode) + "\n"
               + "param:  " + xspcomm::sFmt("0x%x", param) + "\n"
               + "size:   " + xspcomm::sFmt("0x%x", size) + "\n"
               + "source: " + xspcomm::sFmt("0x%x", source) + "\n"
               + "sink:   " + xspcomm::sFmt("0x%x", sink) + "\n"
               + "denied: " + xspcomm::sFmt("0x%x", denied) + "\n" + "data:   "
               + xspcomm::sArrayHex((unsigned char *)data, TLDataSizeS) + "\n"
               + "corrupt:" + xspcomm::sFmt("0x%x", corrupt) + "\n"
               + "valid:  " + xspcomm::sFmt("0x%x", valid) + "\n"
               + "ready:  " + xspcomm::sFmt("0x%x", ready) + "\n";
    }
} TLDataD, *pTLDataD;

void tld_2_port(TLDataD d, xspcomm::XPort &port)
{
    port["d_bits_opcode"] = d.opcode;
    port["d_bits_param"]  = d.param;
    port["d_bits_size"]   = d.size;
    port["d_bits_source"] = d.source;
    port["d_bits_sink"]   = d.sink;
    port["d_bits_denied"] = d.denied;
    port["d_bits_data"].SetBits(d.data, TLDataSizeS);
    port["d_bits_corrupt"] = d.corrupt;
    port["d_valid"]        = 0;
}

TLDataD port_2_tld(xspcomm::XPort &port)
{
    TLDataD d;
    d.opcode = port["d_bits_opcode"];
    d.param  = port["d_bits_param"];
    d.size   = port["d_bits_size"];
    d.source = port["d_bits_source"];
    d.sink   = port["d_bits_sink"];
    d.denied = port["d_bits_denied"];
    port["d_bits_data"].GetBits((u_int32_t *)d.data, TLDataSizeS / 4);
    d.corrupt = port["d_bits_corrupt"];
    d.valid   = port["d_valid"];
    d.ready   = port["d_ready"];
    return d;
}

typedef struct sTLDataE : public TLData {
    sTLDataE() { this->__channel = TLChannel::E; };
    u_int8_t sink  = 0; // i  => FIXME
    u_int8_t valid = 0; // 1
    u_int8_t ready = 0; // 1
    std::string AsString()
    {
        return "\nport:  DataE\nsink:  " + xspcomm::sFmt("0x%x", sink) + "\n"
               + "valid:  " + xspcomm::sFmt("0x%x", valid) + "\n"
               + "ready:  " + xspcomm::sFmt("0x%x", ready) + "\n";
    }
} TLDataE, *pTLDataE;

void tle_2_port(TLDataE e, xspcomm::XPort &port)
{
    port["e_bits_sink"] = e.sink;
    port["e_valid"]     = 0;
}

TLDataE port_2_tle(xspcomm::XPort &port)
{
    TLDataE e;
    e.sink  = port["e_bits_sink"];
    e.valid = port["e_valid"];
    e.ready = port["e_ready"];
    return e;
}

bool tl_fire(xspcomm::XPort &port, TLChannel ch)
{
    switch (ch) {
    case TLChannel::A:
        return port["a_valid"] == 1 && port["a_ready"] == 1;
        break;
    case TLChannel::B:
        return port["b_valid"] == 1 && port["b_ready"] == 1;
        break;
    case TLChannel::C:
        return port["c_valid"] == 1 && port["c_ready"] == 1;
        break;
    case TLChannel::D:
        return port["d_valid"] == 1 && port["d_ready"] == 1;
        break;
    case TLChannel::E:
        return port["e_valid"] == 1 && port["e_ready"] == 1;
        break;
    default: break;
    }
    return false;
}

TLParamType get_TLAParamType(u_int8_t v)
{
    auto a = (TLMessageA)v;
    switch (a) {
    case TLMessageA::LogicalData: return TLParamType::Logical;
    case TLMessageA::AcquireBlock:
    case TLMessageA::AcquirePerm: return TLParamType::Grow;
    case TLMessageA::ArithmeticLogic: return TLParamType::Arithmetic;
    default: break;
    }
    return TLParamType::None;
}

TLParamType get_TLBParamType(u_int8_t v)
{
    auto b = (TLMessageB)v;
    switch (b) {
    case TLMessageB::ProbePerm:
    case TLMessageB::ProbeBlock: return TLParamType::Cap;
    case TLMessageB::ArithmeticData: return TLParamType::Arithmetic;
    case TLMessageB::Intent: return TLParamType::Intent;
    default: break;
    }
    return TLParamType::None;
}

TLParamType get_TLCParamType(u_int8_t v)
{
    auto c = (TLMessageC)v;
    switch (c) {
    case TLMessageC::ProbeAck:
    case TLMessageC::ProbeAckData:
    case TLMessageC::Release:
    case TLMessageC::ReleaseData: return TLParamType::Shrink_Report;
    default: break;
    }
    return TLParamType::None;
}

TLParamType get_TLDParamType(u_int8_t v)
{
    auto d = (TLMessageD)v;
    switch (d) {
    case TLMessageD::Grant:
    case TLMessageD::GrantData: return TLParamType::Cap;
    default: break;
    }
    return TLParamType::None;
}

std::string str_TLData(TLData *v)
{
    if (v == nullptr) return "Null";

    std::string ret;
    switch (v->__channel) {
    case TLChannel::A: {
        auto a = (pTLDataA)v;
        ret    = str_TLMessageA(a->opcode) + "."
              + str_TLParam(a->param, get_TLAParamType(a->opcode))
              + xspcomm::sFmt("(Addr:0x%lx)", a->address);
        break;
    }
    case TLChannel::B: {
        auto b = (pTLDataB)v;
        ret    = str_TLMessageB(b->opcode) + "."
              + str_TLParam(b->param, get_TLBParamType(b->opcode))
              + xspcomm::sFmt("(source: %x)", b->source)
              + xspcomm::sFmt("(Addr:0x%lx)", b->address);
        break;
    }
    case TLChannel::C: {
        auto c = (pTLDataC)v;
        ret    = str_TLMessageC(c->opcode) + "."
              + str_TLParam(c->param, get_TLCParamType(c->opcode))
              + xspcomm::sFmt("(Addr:0x%lx) data[%s]", c->address,
                              xspcomm::sArrayHex(c->data, TLDataSizeS).c_str());
        break;
    }
    case TLChannel::D: {
        auto d = (pTLDataD)v;
        ret    = str_TLMessageD(d->opcode) + "."
              + str_TLParam(d->param, get_TLDParamType(d->opcode))
              + xspcomm::sFmt("(sink:0x%x) data[%s]", d->sink,
                              xspcomm::sArrayHex(d->data, TLDataSizeS).c_str());
        break;
    }
    case TLChannel::E: {
        // sink, valid, ready
        auto e = (pTLDataE)v;
        ret    = str_TLMessageE()
              + xspcomm::sFmt("(sink:0x%x,valid:%d,ready:%d)", e->sink,
                              e->valid, e->ready);
        break;
    }
    default: ret = "TLData(\?\?)"; break;
    }
    return ret;
}

enum class TLCDir { AD, CD, BC, DE, Error };
enum class TLTaskSatate { Empty, Ready, Done };

// callback
class TLPort{};

typedef std::function<bool(TLPort *master, TLData *data_or_req, TLData *resp,
                           void *args)>
    tl_cb;

typedef struct sTLTask {
public:
    std::list<TLData *> data_send;
    int delay_cycles          = 0;
    TLTaskSatate state        = TLTaskSatate::Empty;
    TLCDir dir                = TLCDir::Error;
    void *args                = nullptr;
    std::string desc          = "";
    u_int64_t stage           = 0;
    u_int64_t timestamp       = 0;
    u_int64_t timeout         = 0;
    tl_cb cb                  = nullptr;
    TLChannel current_channel = TLChannel::F;
} TLTask, pTLTask;

#define LIST_POP_HEAD(l)                                                       \
    *l.begin();                                                                \
    l.pop_front();

class TLMasterPort : public TLPort
{
    /*
        TileLink Master Port           TileLink Slave Port
        +-----------------+           +-----------------+
        |  A<->D m->s    A|    ->     |A  key: source   |
        |  B<->C s->m    B|    <-     |B                |
        |  C<->D m->s    C|    ->     |C                |
        |  D<->E s->m    D|    <-     |D                |
        |                E|    ->     |E                |
        +-----------------+           +-----------------+
    */

    int max_pending_reqs;
    int evt_pending_b = false;
    int evt_pending_d = false;
    tl_cb handler_b   = nullptr;
    tl_cb handler_d   = nullptr;

    bool has_BCE = true;

    std::list<TLTask> tsk_list_cb_pre;
    TLTask tsk_cb_a2d;
    TLTask tsk_hd_b2c;
    TLTask tsk_cb_c2d;
    TLTask tsk_hd_d2e;

    bool is_debug = false;

public:
    uint64_t source = 0;
    xspcomm::XClock *clk;
    xspcomm::XPort *port;
    std::string mName;
    std::string mPrefix;

public:
    TLMasterPort(u_int64_t source, std::string name, xspcomm::XPort &dut_port,
                 xspcomm::XClock &clk, std::string prefix, tl_cb cb_b = nullptr,
                 tl_cb cb_d = nullptr, int max_pend = 20, bool has_BCE = true,
                 bool debug = false) :
        source(source),
        max_pending_reqs(max_pend),
        has_BCE(has_BCE),
        is_debug(debug)
    {
        this->mName     = name;
        this->mPrefix   = prefix;
        this->port      = &dut_port.NewSubPort(prefix);
        this->clk       = &clk;
        this->handler_b = cb_b;
        this->handler_d = cb_d;

        // check port.d.width
        Assert(
            (*this->port)["d_bits_data"].mWidth <= TLDataSizeS * 8,
            "TLMasterPort(%s) port.d_bits_data width(%d) is bigger than %ld, please enlarge TLDataMaskS (TLDatasizeS = 64 * TLDataMaskS)))",
            this->mName.c_str(), (*this->port)["d_bits_data"].mWidth,
            TLDataSizeS * 8);

        // check timeout
        auto check_timeout = [this](TLTask &t) {
            if (t.timeout > 0) {
                Assert(this->clk->clk - t.timestamp < t.timeout,
                       "TLMasterPort(%s) task:%s timeout(%ld)!",
                       this->mName.c_str(), t.desc.c_str(), t.timeout)
            };
        };

        // init ACE invalid
        clk.StepRis(
            [this](u_int64_t c, void *args) {
                // Task assign
                for (auto it = this->tsk_list_cb_pre.begin();
                     it != this->tsk_list_cb_pre.end();) {
                    if (it->delay_cycles > 0) {
                        it->delay_cycles -= 1;
                        it++;
                        continue;
                    }
                    switch (it->dir) {
                    case TLCDir::AD:
                        if (this->tsk_cb_a2d.state == TLTaskSatate::Empty) {
                            this->tsk_cb_a2d = *it;
                        } else {
                            it++;
                            continue;
                        }
                        break;
                    case TLCDir::CD:
                        if (this->tsk_cb_c2d.state == TLTaskSatate::Empty) {
                            this->tsk_cb_c2d = *it;
                        } else {
                            it++;
                            continue;
                        }
                        break;
                    default:
                        Assert(false, "Task in pre queue DIR(%d) ERROR",
                               (int)(it->dir)) break;
                    }
                    it = this->tsk_list_cb_pre.erase(it);
                }

                if (tl_fire(*this->port, TLChannel::A))
                    (*this->port)["a_valid"] = 0;
                if (this->has_BCE) {
                    if (tl_fire(*this->port, TLChannel::C))
                        (*this->port)["c_valid"] = 0;
                    if (tl_fire(*this->port, TLChannel::E))
                        (*this->port)["e_valid"] = 0;
                }
                // master side always be ready
                if (this->has_BCE) (*this->port)["b_ready"] = 1;
                (*this->port)["d_ready"] = 1;
                this->evt_pending_b =
                    this->has_BCE ? (*this->port)["b_valid"] == 1 : false;
                this->evt_pending_d = (*this->port)["d_valid"] == 1;
            },
            nullptr,
            "TLMasterPort(" + this->mName
                + (this->has_BCE ? ")_Ris_Init-ACE-invalid" :
                                   ")_Ris_Init-A-invalid"));

        // Do send:
        clk.StepRis(
            [this, check_timeout](u_int64_t c, void *args) {
                if ((*this->port)["a_ready"] == 1) {
                    // send A2D
                    if (this->tsk_cb_a2d.state == TLTaskSatate::Ready) {
                        auto data = LIST_POP_HEAD(this->tsk_cb_a2d.data_send);
                        Assert(data->__channel == TLChannel::A,
                               "Task in A2D need be A channel!");
                        if (this->tsk_cb_a2d.data_send.size() == 0)
                            this->tsk_cb_a2d.state = TLTaskSatate::Done;
                        tla_2_port(*(pTLDataA)data, *this->port);
                        (*this->port)["a_valid"]         = 1;
                        this->tsk_cb_a2d.current_channel = TLChannel::A;
                        DebugC(this->is_debug, "[%ld] %s: Send -> A: %s", c,
                               this->mName.c_str(), str_TLData(data).c_str());
                        delete data;
                    }
                }
                if (this->has_BCE) {
                    if ((*this->port)["c_ready"] == 1) {
                        // send C2D
                        auto busy = false;
                        if (this->tsk_cb_c2d.state == TLTaskSatate::Ready) {
                            auto data =
                                LIST_POP_HEAD(this->tsk_cb_c2d.data_send);
                            Assert(data->__channel == TLChannel::C,
                                   "Task in C2D need be C channel!");
                            if (this->tsk_cb_c2d.data_send.size() == 0)
                                this->tsk_cb_c2d.state = TLTaskSatate::Done;
                            tlc_2_port(*(pTLDataC)data, *this->port);
                            (*this->port)["c_valid"]         = 1;
                            this->tsk_cb_c2d.current_channel = TLChannel::C;
                            DebugC(this->is_debug, "[%ld] %s: Send -> C: %s", c,
                                   this->mName.c_str(),
                                   str_TLData(data).c_str());
                            busy = true;
                            delete data;
                        }
                        // return B2C
                        if (!busy) {
                            if (this->tsk_hd_b2c.state == TLTaskSatate::Ready) {
                                auto data =
                                    LIST_POP_HEAD(this->tsk_hd_b2c.data_send);
                                Assert(data->__channel == TLChannel::C,
                                       "Task in B2C need be B channel!");
                                if (this->tsk_hd_b2c.data_send.size() == 0)
                                    this->tsk_hd_b2c.state =
                                        TLTaskSatate::Empty;
                                tlc_2_port(*(pTLDataC)data, *this->port);
                                (*this->port)["c_valid"]         = 1;
                                this->tsk_hd_b2c.current_channel = TLChannel::C;
                                DebugC(this->is_debug,
                                       "[%ld] %s: Resp -> C: %s", c,
                                       this->mName.c_str(),
                                       str_TLData(data).c_str());
                                delete data;
                            }
                        }
                    }
                    if ((*this->port)["e_ready"] == 1) {
                        // send D->E ACK: full path is A->D->E
                        if (this->tsk_hd_d2e.state == TLTaskSatate::Ready) {
                            auto data =
                                LIST_POP_HEAD(this->tsk_hd_d2e.data_send);
                            Assert(data->__channel == TLChannel::E,
                                   "Task in D2E need be D channel!");
                            if (this->tsk_hd_d2e.data_send.size() == 0)
                                this->tsk_hd_d2e.state = TLTaskSatate::Done;
                            tle_2_port(*(pTLDataE)data, *this->port);
                            (*this->port)["e_valid"]         = 1;
                            this->tsk_hd_d2e.current_channel = TLChannel::E;
                            DebugC(this->is_debug, "[%ld] %s: Resp -> E: %s", c,
                                   this->mName.c_str(),
                                   str_TLData(data).c_str());
                            delete data;
                        }
                    }
                }
            },
            nullptr,
            "TLMasterPort(" + this->mName
                + (this->has_BCE ? ")_Ris_DoSend-ACE" : ")_Ris_DoSend-A"));

        // Do recv:
        // callback: C->D ACKS
        clk.StepRis(
            [this](u_int64_t c, void *args) {
                if ((*this->port)["d_valid"] == 1
                    && tl_message_d_is_ack(*this->port, this->source)
                    && this->has_BCE) {
                    Assert(this->tsk_cb_c2d.state != TLTaskSatate::Empty,
                           "TLMasterPort(%s) task:%s is Empty!",
                           this->mName.c_str(), this->tsk_cb_c2d.desc.c_str());
                    sTLDataD d          = port_2_tld(*this->port);
                    this->evt_pending_d = false;
                    this->tsk_cb_c2d.cb(this, &d,
                                        (TLData *)this->tsk_cb_c2d.stage,
                                        this->tsk_cb_c2d.args);
                    this->tsk_cb_c2d.stage += 1;
                    DebugC(this->is_debug, "[%ld] %s: Recv -> D: %s", c,
                           this->mName.c_str(), str_TLData(&d).c_str());
                }
                // clear C2D->E
                if ((*this->port)["d_valid"] == 0
                    && this->tsk_cb_c2d.state == TLTaskSatate::Done
                    && tsk_cb_c2d.stage > 0 && this->has_BCE) {
                    this->tsk_cb_c2d.stage = 0;
                    this->tsk_cb_c2d.state = TLTaskSatate::Empty;
                }
            },
            nullptr, "TLMasterPort(" + this->mName + ")_Ris_C2D-ACK-callback");

        // callback: channel A->D
        clk.StepRis(
            [this](u_int64_t c, void *args) {
                if ((*this->port)["d_valid"] == 1
                    && tl_message_d_is_request(*this->port, this->source)) {
                    Assert(this->tsk_cb_a2d.state != TLTaskSatate::Empty,
                           "TLMasterPort(%s) task:%s is Empty!",
                           this->mName.c_str(), this->tsk_cb_a2d.desc.c_str());
                    sTLDataD d = port_2_tld(*this->port);

                    this->evt_pending_d = false;
                    this->tsk_cb_a2d.cb(this, &d,
                                        (TLData *)this->tsk_cb_a2d.stage,
                                        this->tsk_cb_a2d.args);
                    this->tsk_cb_a2d.stage += 1;

                    // send D->E ACK
                    if (this->tsk_hd_d2e.state == TLTaskSatate::Empty) {
                        Assert(this->has_BCE, "TLMasterPort(%s) has no BCE!",
                               this->mName.c_str());
                        Assert(this->tsk_hd_d2e.data_send.size() == 0,
                               "TLMasterPort(%s) task:%s has pending data!",
                               this->mName.c_str(),
                               this->tsk_hd_d2e.desc.c_str());
                        this->tsk_hd_d2e.state = TLTaskSatate::Ready;
                        this->tsk_hd_d2e.desc  = "Task of TLMasterPort("
                                                + this->mName
                                                + ")_Ris_A2D-callback";
                        this->tsk_hd_d2e.timestamp = this->clk->clk;
                        auto ack                   = new TLDataE();
                        ack->sink                  = d.sink;
                        ack->valid                 = 1;
                        ack->ready                 = 1;
                        this->tsk_hd_d2e.data_send.push_back(ack);
                    }
                    DebugC(this->is_debug, "[%ld] %s: Recv -> D: %s", c,
                           this->mName.c_str(), str_TLData(&d).c_str());
                }
                if ((*this->port)["d_valid"] == 0
                    && this->tsk_hd_d2e.state == TLTaskSatate::Done
                    && this->tsk_cb_a2d.state == TLTaskSatate::Done) {
                    // clear A2D->E
                    this->tsk_hd_d2e.state = TLTaskSatate::Empty;
                    this->tsk_cb_a2d.state = TLTaskSatate::Empty;
                    this->tsk_cb_a2d.stage = 0;
                }
            },
            nullptr, "TLMasterPort(" + this->mName + ")_Ris_A2D-callback");

        // handler: B->C
        if (this->has_BCE) {
            clk.StepRis(
                [this](u_int64_t c, void *args) {
                    if ((*this->port)["b_valid"] == 0) return;
                    // create task
                    sTLDataB req = port_2_tlb(*this->port);
                    bool repeat  = false;
                    TLTask tsk;
                    tsk.state = TLTaskSatate::Ready;
                    DebugC(this->is_debug, "[%ld] %s: Recv -> B: %s", c,
                           this->mName.c_str(), str_TLData(&req).c_str());
                    do {
                        auto resp           = new TLDataC();
                        resp->__valid       = true;
                        resp->source        = req.source;
                        this->evt_pending_b = false;
                        if (this->handler_b) {
                            repeat = this->handler_b(this, &req, resp, args);
                        }
                        tsk.data_send.push_back(resp);
                    } while (repeat);
                    // response
                    tsk.dir  = TLCDir::BC;
                    tsk.desc = "Task of TLMasterPort(" + this->mName
                               + ")_Ris_B2C-handler";
                    tsk.timestamp       = this->clk->clk;
                    this->tsk_hd_b2c    = tsk;
                    this->evt_pending_b = false;
                },
                nullptr, "TLMasterPort(" + this->mName + ")_Ris_B2C-handler");
        }

        // On event END check
        clk.StepRis(
            [this](u_int64_t c, void *args) {
                if (this->evt_pending_b) {
                    sTLDataB b = port_2_tlb(*this->port);
                    Warn(
                        "[%ld] TLMasterPort(%s).B has pending Data: %s, source=%lx, please do handler/callback check!",
                        c, this->mName.c_str(),
                        str_TLMessageB(b.opcode).c_str(), b.source);
                }
                if (this->evt_pending_d) {
                    sTLDataD d = port_2_tld(*this->port);
                    Warn(
                        "[%ld] TLMasterPort(%s).D has pending Data: %s, source=%lx, please do handler/callback check!",
                        c, this->mName.c_str(),
                        str_TLMessageD(d.opcode).c_str(), d.source);
                }
            },
            nullptr, "TLMasterPort(" + this->mName + ")_Ris_OnEventCheck");
    };
    ~TLMasterPort() { delete this->port; };

    void SetDebug(bool debug = true) { this->is_debug = debug; }

    bool SendReqA2D(TLDataA *a, int a_size, tl_cb cb = nullptr,
                    void *args = nullptr, std::string desc = "",
                    u_int64_t delay_cycles = 0, u_int64_t timeout = 0)
    {
        if (this->tsk_list_cb_pre.size() >= this->max_pending_reqs)
            return false;

        TLTask tsk;
        tsk.state        = TLTaskSatate::Ready;
        tsk.delay_cycles = delay_cycles;
        tsk.dir          = TLCDir::AD;
        tsk.cb           = cb;
        tsk.args         = args;
        tsk.desc      = "TLMasterPort(" + this->mName + ")_SendReqA2D:" + desc;
        tsk.timeout   = timeout;
        tsk.timestamp = this->clk->clk;
        for (int i = 0; i < a_size; i++) {
            auto data     = new TLDataA(a[i]);
            data->__valid = true;
            data->source  = this->source;
            tsk.data_send.push_back(data);
        }
        this->tsk_list_cb_pre.push_back(tsk);
        return true;
    };

    bool SendReqC2D(TLDataC *c, int c_size, tl_cb cb = nullptr,
                    void *args = nullptr, std::string desc = "",
                    u_int64_t delay_cycles = 0, u_int64_t timeout = 0)
    {
        Assert(this->has_BCE, "SendReqC2D need has_BCE=true!");
        if (this->tsk_list_cb_pre.size() >= this->max_pending_reqs)
            return false;
        TLTask tsk;
        tsk.state        = TLTaskSatate::Ready;
        tsk.delay_cycles = delay_cycles;
        tsk.dir          = TLCDir::CD;
        tsk.cb           = cb;
        tsk.args         = args;
        tsk.desc      = "TLMasterPort(" + this->mName + ")_SendReqC2D:" + desc;
        tsk.timeout   = timeout;
        tsk.timestamp = this->clk->clk;
        for (int i = 0; i < c_size; i++) {
            auto data     = new TLDataC(c[i]);
            data->__valid = true;
            data->source  = this->source;
            tsk.data_send.push_back(data);
        }
        this->tsk_list_cb_pre.push_back(tsk);
        return true;
    };
};

/********* Coroutine version ******************/
class TLMasterPortCor : public TLPort
{
    void *handler_b_args = nullptr;
    tl_cb handler_b      = nullptr;
    bool has_BCE         = true;

public:
    std::string mName;
    std::string mPrefix;
    xspcomm::XPort *port;
    xspcomm::XClock *clk;
    bool is_debug = false;
    TLMasterPortCor(u_int64_t source, std::string name,
                    xspcomm::XPort &dut_port, xspcomm::XClock &clk,
                    std::string prefix, tl_cb cb_b = nullptr,
                    bool has_BCE = true, bool debug = false)
    {
        this->mName     = name;
        this->mPrefix   = prefix;
        this->port      = &dut_port.NewSubPort(prefix);
        this->clk       = &clk;
        this->handler_b = cb_b;
        this->has_BCE   = has_BCE;
        this->is_debug  = debug;
#if !ENABLE_XCOROUTINE
#pragma message("TLMasterPortCor need C++20 or later!")
        Assert(false, "TLMasterPortCor need C++20 or later!");
#else
#pragma message("TLMasterPortCor is enabled!")
        this->b_handler();
#endif
    }

#if ENABLE_XCOROUTINE

    xspcomm::xcorutine<> b_handler()
    {
        (*this->port)["d_ready"] = 1;
        if (this->has_BCE) { (*this->port)["b_ready"] = 1; }
        while (true && this->has_BCE) {
            co_await xspcomm::XStep(*this->clk, 1);
            // handler: B->C
            if ((*this->port)["b_valid"] == 1) {
                // create task
                sTLDataB req = port_2_tlb(*this->port);
                bool repeat  = false;
                DebugC(this->is_debug, "[%ld] %s: Recv <- B: %s",
                       this->clk->clk, this->mName.c_str(),
                       str_TLData(&req).c_str());
                do {
                    if (repeat) co_await xspcomm::XStep(*this->clk, 1);
                    auto resp     = new TLDataC();
                    resp->__valid = true;
                    resp->source  = req.source;
                    if (this->handler_b) {
                        repeat = this->handler_b(this, &req, resp,
                                                 this->handler_b_args);
                    }
                    tlc_2_port(*(pTLDataC)resp, *this->port);
                    (*this->port)["d_valid"] = 1;
                    DebugC(this->is_debug, "[%ld] %s: Resp -> C: %s",
                           this->clk->clk, this->mName.c_str(),
                           str_TLData(resp).c_str());
                    delete resp;
                } while (repeat);
            }
        }
    }

    xspcomm::xcorutine<std::vector<TLDataD>> SendA2D(TLDataA a)
    {
        std::vector<TLDataD> ret;
        // Send
        co_await xspcomm::XCondition(
            [this]() { return (*this->port)["a_ready"] == 1; });
        tla_2_port(a, *this->port);
        (*this->port)["a_valid"] = 1;
        DebugC(this->is_debug, "[%ld] %s: Send -> A: %s", this->clk->clk,
               this->mName.c_str(), str_TLData(&a).c_str());
        co_await xspcomm::XStep(*this->clk, 1);
        (*this->port)["a_valid"] = 0;
        // ack
        bool ack_ed = false;
        auto ack    = new TLDataE();
        ack->valid  = 1;
        ack->ready  = 1;
        // Recv
        co_await xspcomm::XCondition(
            [this]() { return (*this->port)["d_valid"] == 1; });
        auto d = port_2_tld(*this->port);
        DebugC(this->is_debug, "[%ld] %s: Recv <- D: %s", this->clk->clk,
               this->mName.c_str(), str_TLData(&d).c_str());
        ack->sink = d.sink;
        if (this->has_BCE) {
            // send ack
            if ((*this->port)["e_ready"] == 1 && false) {
                tle_2_port(*ack, *this->port);
                (*this->port)["e_valid"] = 1;
                ack_ed                   = true;
                DebugC(this->is_debug, "[%ld] %s: Send -> E: %s",
                       this->clk->clk, this->mName.c_str(),
                       str_TLData(ack).c_str());
            }
        }
        ret.push_back(d);
        // check burst
        while (true) {
            co_await xspcomm::XStep(*this->clk, 1);
            if (ack_ed) (*this->port)["e_valid"] = 0;
            if ((*this->port)["d_valid"] == 0) break;
            d = port_2_tld(*this->port);
            ret.push_back(d);
            DebugC(this->is_debug, "[%ld] %s: Recv <- D: %s", this->clk->clk,
                   this->mName.c_str(), str_TLData(&d).c_str());
        }
        // send ack
        if (this->has_BCE) {
            if ((*this->port)["e_ready"] == 1 && ack_ed == false) {
                tle_2_port(*ack, *this->port);
                (*this->port)["e_valid"] = 1;
                DebugC(this->is_debug, "[%ld] %s: Send -> E: %s",
                       this->clk->clk, this->mName.c_str(),
                       str_TLData(ack).c_str());
                ack_ed = true;
            }
            // send ack
            if (!ack_ed) {
                co_await xspcomm::XCondition(
                    [this]() { return (*this->port)["e_ready"] == 1; });
                tle_2_port(*ack, *this->port);
                (*this->port)["e_valid"] = 1;
                DebugC(this->is_debug, "[%ld] %s: Send -> E: %s",
                       this->clk->clk, this->mName.c_str(),
                       str_TLData(ack).c_str());
            }
            co_await xspcomm::XStep(*this->clk, 1);
            if (ack_ed) (*this->port)["e_valid"] = 0;
        }
        co_return ret;
    }

    xspcomm::xcorutine<TLDataD> SendC2D(std::vector<TLDataC> data)
    {
        TLDataD ret;
        co_await xspcomm::XStep(*this->clk, 1);
        // Send
        for (auto &c : data) {
            co_await xspcomm::XCondition(
                [this]() { return (*this->port)["c_ready"] == 1; });
            tlc_2_port(c, *this->port);
            (*this->port)["c_valid"] = 1;
            DebugC(this->is_debug, "[%ld] %s: Send -> C: %s", this->clk->clk,
                   this->mName.c_str(), str_TLData(&c).c_str());

            co_await xspcomm::XStep(*this->clk, 1);
            if ((*this->port)["d_valid"] == 1) {
                DebugC(this->is_debug, "[%ld] %s: Recv -> D: %s",
                       this->clk->clk, this->mName.c_str(),
                       str_TLData(&ret).c_str());
                ret         = port_2_tld(*this->port);
                ret.__valid = true;
            }
        }
        (*this->port)["c_valid"] = 0;
        // Recv
        if (ret.__valid) co_return ret;
        co_await xspcomm::XCondition(
            [this]() { return (*this->port)["d_valid"] == 1; });
        auto d = port_2_tld(*this->port);
        DebugC(this->is_debug, "[%ld] %s: Recv -> D: %s", this->clk->clk,
               this->mName.c_str(), str_TLData(&d).c_str());
        co_return d;
    }
#else
    void b_handler(){
        Assert(false, "TLMasterPortCor need C++20 or later!")
    }
#endif
};
/****************************************/

} // namespace ovip
#endif
